<?php

/** Zend_Controller_Exception */
require_once 'Framework/Controller/Action/Exception.php';

/** Zend_Controller_Action_HelperBroker */
require_once 'Framework/Controller/Action/HelperBroker.php';

/**
 * Framework
 * 
 * Zend_Controller_Action предоставляет простейший и гибкий механизм интеграции видов. Два метода
 * осуществляют это: initView() и render(). Первый метод выполняет отложенную загрузку открытого
 * свойства $view, второй выполняет рендеринг вида, основываясь на запрошенном в данный момент действии,
 * используя иерархию директорий для определения пути к скрипту.
 * 
 * @category   Framework
 * @package    Framework_Controller
 */

abstract class Framework_Controller_Action
{
    /**
     * Framework_Controller_Request_Abstract объект обертка для response
     * 
     * @var Framework_Controller_Request_Abstract
     */
    private $_request = null;

    /**
     * Framework_Controller_Response_Abstract объект обертка для request
     * 
     * @var Framework_Controller_Response_Abstract
     */
    private $_response = null;

    /**
     * View объект обертка для smarty
     * 
     * @var Framework_View
     */
    protected $_view;

    /**
     * Helper Broker to assist in routing help requests to the proper object
     *
     * @var Zend_Controller_Action_HelperBroker
     */
    protected $_helper = null;
    
    /**
     * Array of arguments provided to the constructor, minus the
     * {@link $_request Request object}.
     * @var array
     */
    private $_invokeArgs = array();

     /**
     * Front controller instance
     * @var Zend_Controller_Front
     */
    protected $_frontController;
    
    protected $_config;
    
    /**
     * Framework_Controller_Action
     * 
     */
    function __construct(Framework_Controller_Request_Abstract $request, Framework_Controller_Response_Abstract $response)
    {
        $this->_helper = new Framework_Controller_Action_HelperBroker($this);
        $this->setRequest($request)
             ->setResponse($response);
        $this->_config = Framework_Registry::get('config');
        $this->_initView();
        $this->init();
        
    }

    /**
     * Return a single invocation argument
     *
     * @param string $key
     * @return mixed
     */
    public function getInvokeArg($key)
    {
        if (isset($this->_invokeArgs[$key])) {
            return $this->_invokeArgs[$key];
        }

        return null;
    }
    
    /**
     * Return the Request object
     *
     * @return Zend_Controller_Request_Abstract
     */
    public function getRequest()
    {
        return $this->_request;
    }

    /**
     * Set the Request object
     *
     * @param Zend_Controller_Request_Abstract $request
     * @return Zend_Controller_Action
     */
    public function setRequest(Framework_Controller_Request_Abstract $request)
    {
        $this->_request = $request;
        return $this;
    }

    /**
     * Return the Response object
     *
     * @return Zend_Controller_Response_Abstract
     */
    public function getResponse()
    {
        return $this->_response;
    }

    /**
     * Set the Response object
     *
     * @param Zend_Controller_Response_Abstract $response
     * @return Zend_Controller_Action
     */
    public function setResponse(Framework_Controller_Response_Abstract $response)
    {
        $this->_response = $response;
        return $this;
    }
    
    private function _initView()
    {
        if ($this->getInvokeArg('noViewRenderer')) {
            return $this->_view;
        }
        
        $request = $this->getRequest();
        $dir     = $this->getFrontController()->getControllerDirectory();

        if ($this->getRequest()->has('admin')) {
            $base_dir = preg_replace('[application/(.+)]si', '', $dir) . DIRECTORY_SEPARATOR . 'templates/admin/';
        } else {
            $base_dir = dirname($dir) . DIRECTORY_SEPARATOR . 'templates/' . $$config->smarty->template_dir;
        }

        require_once 'Framework/View.php';
        $this->_view = new Framework_View($base_dir);

        return $this;
    }
    
    /**
     * Render a view
     *
     * @param  $path
     * @return void
     */
    final public function render($path = null)
    {
        // Системные переменные smarty
        $this->_view->sys_full_path   = trim($this->_request->getRequestUri(), '/\\');
        $this->_view->sys_path        = trim($this->_request->getServer('REDIRECT_URL'), '/\\');
        $this->_view->sys_action_path = trim($this->_config->www->baseurl, '/\\')
                                        . '/' . $this->_request->getControllerName()
                                        . '/' . $this->_request->getActionName();
        $this->_view->sys_controller  = $this->_request->getControllerName();
        $this->_view->sys_action      = $this->_request->getActionName();

        $this->getResponse()->sendResponse();

        if (null === $path) {
            $path = $this->_request->getControllerName() . '/' . $this->_request->getActionName() . '.tpl';
        }
        
        $this->_view->display($path);
    }

    /**
     * Redirect to another URL
     *
     * @param string $url
     * @return void
     */
    protected function _redirect($url = null)
    {
        $url = empty($url) ? $this->_request->getRequestUri() : $url;
        header('Location: '. $url);
        exit();
    }
    
    /**
     * Proxy for undefined methods.  Default behavior is to throw an
     * exception on undefined methods, however this function can be
     * overridden to implement magic (dynamic) actions, or provide run-time
     * dispatching.
     *
     * @param string $methodName
     * @param array $args
     */
    public function __call($methodName, $args)
    {
        if ('Action' == substr($methodName, -6)) {
            require_once 'Framework/Controller/Action/Exception.php';
            $action = substr($methodName, 0, strlen($methodName) - 6);
            throw new Framework_Controller_Action_Exception(sprintf('Action "%s" does not exist and was not trapped in __call()', $action), 404);
        }

        require_once 'Framework/Controller/Action/Exception.php';
        throw new Framework_Controller_Action_Exception(sprintf('Method "%s" does not exist and was not trapped in __call()', $methodName), 500);
    }
    
    
    /**
     * Pre-dispatch routines
     *
     * Called before action method. If using class with
     * {@link Zend_Controller_Front}, it may modify the
     * {@link $_request Request object} and reset its dispatched flag in order
     * to skip processing the current action.
     *
     * @return void
     */
    public function preDispatch()
    {
    }

    /**
     * Post-dispatch routines
     *
     * Called after action method execution. If using class with
     * {@link Zend_Controller_Front}, it may modify the
     * {@link $_request Request object} and reset its dispatched flag in order
     * to process an additional action.
     *
     * Common usages for postDispatch() include rendering content in a sitewide
     * template, link url correction, setting headers, etc.
     *
     * @return void
     */
    public function postDispatch()
    {
    }
    
    /**
     * Dispatch the requested action
     *
     * @param string $action Method name of action
     * @return void
     */
    public function dispatch($action)
    {
        // Notify helpers of action preDispatch state
        $this->_helper->notifyPreDispatch();

        $this->preDispatch();

        if (in_array($action, get_class_methods($this))) {
            $this->$action();
        } else {
            $this->__call($action, array());
        }
        $this->postDispatch();

        // whats actually important here is that this action controller is
        // shutting down, regardless of dispatching; notify the helpers of this
        // state
        $this->_helper->notifyPostDispatch();
    }
    
    /**
     * Retrieve Front Controller
     *
     * @return Zend_Controller_Front
     */
    public function getFrontController()
    {
        // Used cache version if found
        if (null !== $this->_frontController) {
            return $this->_frontController;
        }

        // Grab singleton instance, if class has been loaded
        if (class_exists('Framework_Controller_Front')) {
            $this->_frontController = Framework_Controller_Front::getInstance();
            return $this->_frontController;
        }

        // Throw exception in all other cases
        require_once 'Framework/Controller/Exception.php';
        throw new Framework_Controller_Exception('Front controller class has not been loaded');
    }
    
    public function init() {}
}